/*-------------------------------------------------------------------------
 *
 * sortsupport.c
 *	  加速排序的支持例程。
 *
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 * IDENTIFICATION
 *	  src/backend/utils/sort/sortsupport.c
 *
 *-------------------------------------------------------------------------
 */

#include "postgres.h"

#include "access/gist.h"
#include "access/nbtree.h"
#include "catalog/pg_am.h"
#include "fmgr.h"
#include "utils/lsyscache.h"
#include "utils/rel.h"
#include "utils/sortsupport.h"


/* 需要的信息以使用旧式比较函数作为排序比较器 */
typedef struct
{
	FmgrInfo	flinfo;			/* 用于比较函数的查找数据 */
	FunctionCallInfoBaseData fcinfo;	/* 可重用的调用信息结构 */
} SortShimExtra;

#define SizeForSortShimExtra(nargs) (offsetof(SortShimExtra, fcinfo) + SizeForFunctionCallInfo(nargs))

/*
 * 用于调用旧式比较器的适配函数
 *
 * 这基本上是 FunctionCall2Coll() 的内联版本，除了
 * 我们假设 FunctionCallInfoBaseData 是由 PrepareSortSupportComparisonShim
 * 预先大部分设置好的。
 */
static int fc_comparison_shim(Datum fc_x, Datum fc_y, SortSupport fc_ssup)
{
	SortShimExtra *fc_extra = (SortShimExtra *) fc_ssup->ssup_extra;
	Datum		fc_result;

	fc_extra->fcinfo.args[0].value = fc_x;
	fc_extra->fcinfo.args[1].value = fc_y;

	/* 只是为了以防万一，我们每次都重置 isnull */
	fc_extra->fcinfo.isnull = false;

	fc_result = FunctionCallInvoke(&fc_extra->fcinfo);

	/* 检查结果是否为 null，因为调用者显然不期望出现这种情况 */
	if (fc_extra->fcinfo.isnull)
		elog(ERROR, "function %u returned NULL", fc_extra->flinfo.fn_oid);

	return fc_result;
}

/*
 * 设置一个适配函数，以允许将旧式 btree 比较函数
 * 作为排序支持比较器使用。
 */
void PrepareSortSupportComparisonShim(Oid fc_cmpFunc, SortSupport fc_ssup)
{
	SortShimExtra *fc_extra;

	fc_extra = (SortShimExtra *) MemoryContextAlloc(fc_ssup->ssup_cxt,
												 SizeForSortShimExtra(2));

	/* 查找比较函数 */
	fmgr_info_cxt(fc_cmpFunc, &fc_extra->flinfo, fc_ssup->ssup_cxt);

	/* 我们可以只初始化一次调用信息并重用它 */
	InitFunctionCallInfoData(fc_extra->fcinfo, &fc_extra->flinfo, 2,
							 fc_ssup->ssup_collation, NULL, NULL);
	fc_extra->fcinfo.args[0].isnull = false;
	fc_extra->fcinfo.args[1].isnull = false;

	fc_ssup->ssup_extra = fc_extra;
	fc_ssup->comparator = fc_comparison_shim;
}

/*
 * 查找并调用排序支持函数以设置 SortSupport 比较器；
 * 或者如果没有这样的函数存在或它拒绝设置适当的
 * 状态，则准备一个合适的适配函数。
 */
static void fc_FinishSortSupportFunction(Oid fc_opfamily, Oid fc_opcintype, SortSupport fc_ssup)
{
	Oid			fc_sortSupportFunction;

	/* 查找排序支持函数 */
	fc_sortSupportFunction = get_opfamily_proc(fc_opfamily, fc_opcintype, fc_opcintype,
											BTSORTSUPPORT_PROC);
	if (OidIsValid(fc_sortSupportFunction))
	{
		/*
		 * 排序支持函数可以提供比较器，但它也可以选择不
		 * 提供（例如，基于所选的整理方式）。
		 */
		OidFunctionCall1(fc_sortSupportFunction, PointerGetDatum(fc_ssup));
	}

	if (fc_ssup->comparator == NULL)
	{
		Oid			fc_sortFunction;

		fc_sortFunction = get_opfamily_proc(fc_opfamily, fc_opcintype, fc_opcintype,
										 BTORDER_PROC);

		if (!OidIsValid(fc_sortFunction))
			elog(ERROR, "missing support function %d(%u,%u) in opfamily %u",
				 BTORDER_PROC, fc_opcintype, fc_opcintype, fc_opfamily);

		/* 我们将使用适配函数来调用旧式 btree 比较器 */
		PrepareSortSupportComparisonShim(fc_sortFunction, fc_ssup);
	}
}

/*
 * 给定一个排序操作符（btree "<" 或 ">" 操作符）填充 SortSupport。
 *
 * 调用者必须先将 SortSupportData 结构置零，然后再填充
 * ssup_cxt、ssup_collation 和 ssup_nulls_first。这将填充
 * ssup_reverse 以及比较函数指针。
 */
void PrepareSortSupportFromOrderingOp(Oid fc_orderingOp, SortSupport fc_ssup)
{
	Oid			fc_opfamily;
	Oid			fc_opcintype;
	int16		fc_strategy;

	Assert(fc_ssup->comparator == NULL);

	/* 在 pg_amop 中查找操作符 */
	if (!get_ordering_op_properties(fc_orderingOp, &fc_opfamily, &fc_opcintype,
									&fc_strategy))
		elog(ERROR, "operator %u is not a valid ordering operator",
			 fc_orderingOp);
	fc_ssup->ssup_reverse = (fc_strategy == BTGreaterStrategyNumber);

	fc_FinishSortSupportFunction(fc_opfamily, fc_opcintype, fc_ssup);
}

/*
 * 给定一个索引关系、属性和策略填充 SortSupport。
 *
 * 调用者必须先将 SortSupportData 结构置零，然后再填充
 * ssup_cxt、ssup_attno、ssup_collation 和 ssup_nulls_first。这
 * 将填充 ssup_reverse（基于提供的策略），以及比较函数指针。
 */
void PrepareSortSupportFromIndexRel(Relation fc_indexRel, int16 fc_strategy,
							   SortSupport fc_ssup)
{
	Oid			fc_opfamily = fc_indexRel->rd_opfamily[fc_ssup->ssup_attno - 1];
	Oid			fc_opcintype = fc_indexRel->rd_opcintype[fc_ssup->ssup_attno - 1];

	Assert(fc_ssup->comparator == NULL);

	if (fc_indexRel->rd_rel->relam != BTREE_AM_OID)
		elog(ERROR, "unexpected non-btree AM: %u", fc_indexRel->rd_rel->relam);
	if (fc_strategy != BTGreaterStrategyNumber &&
		fc_strategy != BTLessStrategyNumber)
		elog(ERROR, "unexpected sort support strategy: %d", fc_strategy);
	fc_ssup->ssup_reverse = (fc_strategy == BTGreaterStrategyNumber);

	fc_FinishSortSupportFunction(fc_opfamily, fc_opcintype, fc_ssup);
}

/*
 * 给定一个 GiST 索引关系填充 SortSupport
 *
 * 调用者必须先将 SortSupportData 结构置零，然后再填充
 * ssup_cxt、ssup_attno、ssup_collation 和 ssup_nulls_first。这
 * 将填充 ssup_reverse（始终为 GiST 索引构建时为 false），以及
 * 比较函数指针。
 */
void PrepareSortSupportFromGistIndexRel(Relation fc_indexRel, SortSupport fc_ssup)
{
	Oid			fc_opfamily = fc_indexRel->rd_opfamily[fc_ssup->ssup_attno - 1];
	Oid			fc_opcintype = fc_indexRel->rd_opcintype[fc_ssup->ssup_attno - 1];
	Oid			fc_sortSupportFunction;

	Assert(fc_ssup->comparator == NULL);

	if (fc_indexRel->rd_rel->relam != GIST_AM_OID)
		elog(ERROR, "unexpected non-gist AM: %u", fc_indexRel->rd_rel->relam);
	fc_ssup->ssup_reverse = false;

	/*
	 * 查找排序支持函数。这比 B-tree 更简单
	 * 因为我们不支持旧式 btree 比较器。
	 */
	fc_sortSupportFunction = get_opfamily_proc(fc_opfamily, fc_opcintype, fc_opcintype,
											GIST_SORTSUPPORT_PROC);
	if (!OidIsValid(fc_sortSupportFunction))
		elog(ERROR, "missing support function %d(%u,%u) in opfamily %u",
			 GIST_SORTSUPPORT_PROC, fc_opcintype, fc_opcintype, fc_opfamily);
	OidFunctionCall1(fc_sortSupportFunction, PointerGetDatum(fc_ssup));
}
